import { track, trigger } from "./effect"
import { TrackOpType, TriggerOpType } from "./const"
import { isArray, isPlainObject } from "./utils"
import { reactive } from "./reactive"


function convert<T>(value:T) {
  return isPlainObject(value) ? reactive(value) : value
}


class RefImpl<T> {
  constructor(private _value:T, private shallow:boolean) {}

  get value() {
    track(this, TrackOpType.GET, 'value')
    return convert(this._value)
  }

  set value(newValue:T) {
    if(newValue === this._value) return
    this.value = convert(newValue)
    trigger(this, TriggerOpType.SET, 'value')
  }
}

class ObjectRefImpl<T> {
  constructor(private _target:T, private _key:keyof T) {}

  get value() {
    return this._target[this._key]
  }

  set value(newValue) {
    this._target[this._key] = newValue
  }
}

function createRefObj<T>(target:T, shallow:boolean=false) {
  return new RefImpl(target, shallow)
}

export function ref<T>(value:T) {
  return createRefObj(value)
}

export function shallowRef<T>(value:T) {
  return createRefObj(value, true)
}

export function toRef<T extends object, K extends keyof T>(target:T, key:K) {
  return new ObjectRefImpl(target, key)
}

export function toRefs<T extends object>(target:T) {
  const obj = isArray(target) ? [] : {}

  for(const key in target) {
    Reflect.set(obj, key, toRef(target, key))
  }

  return obj
}

export function triggerRef() {}

